package tech

import (
	"fmt"
	"slices"
	"ssp/set"
)

func (s *Solve) UniqueRectangle9(g *Grid) (used bool, results []*Result) {
	return urTemplate9(g, g.doURType9)
}

func (g *Grid) getURP19(p0 *Cell) (p1s []*Cell) {
	for _, cell := range g.rows[p0.rowNum].cells {
		if cell.solved() || cell == p0 {
			continue
		}
		if cell.cands.Count() != 2 {
			continue
		}
		/*if cell.cands.Intersect(p0.cands).Count() != 1 {
			continue
		}*/
		if cell.cands != p0.cands {
			continue
		}
		p1s = append(p1s, cell)
	}
	return
}

func (g *Grid) getURP29(p0 *Cell) (p2s []*Cell) {
	for _, cell := range g.cols[p0.colNum].cells {
		if cell.solved() || cell == p0 {
			continue
		}
		/*if cell.cands.Count() != 2 {
			continue
		}*/
		if cell.cands.Intersect(p0.cands).Count() != 1 {
			continue
		}
		p2s = append(p2s, cell)
	}
	return
}

func (g *Grid) getURP39(p0, p1, p2 *Cell) *Cell {
	p3 := g.rows[p2.rowNum].cells[p1.colNum]
	if p3.solved() {
		return nil
	}
	if !p3.cands.IsSuperset(p0.cands) {
		return nil
	}

	return p3
}

func (g *Grid) getUR9() (rects [][]*Cell) {
	p0s := g.getURP0()
	for _, p0 := range p0s {
		p1s := g.getURP19(p0)
		p2s := g.getURP29(p0)
		for _, p1 := range p1s {
			for _, p2 := range p2s {
				if !withinTwoBoxes([]int{p0.rowNum, p2.rowNum}, []int{p0.colNum, p1.colNum}) {
					continue
				}
				p3 := g.getURP39(p0, p1, p2)
				if p3 != nil {
					rects = append(rects, []*Cell{p0, p1, p2, p3})
				}
			}
		}
	}
	return
}

func urTemplate9(g *Grid, doURType func([]*Cell) (bool, []*Result)) (used bool, results []*Result) {
	rects := g.getUR9()
	for _, rect := range rects {
		if ok, rs := doURType(rect); ok {
			used = true
			if OUTPUTRESULT {
				results = append(results, rs...)
			}
			if !EXHAUSTIVE {
				return
			}
		}
	}
	return
}

func (g *Grid) doURType9(rect []*Cell) (used bool, results []*Result) {
	zCells := []*Cell{rect[2], rect[3]}

	var result *Result
	usedLine, resultLine := g.doURType3Line(rect, zCells)
	if usedLine {
		result = resultLine
	}
	usedBox, resultBox := g.doURType3Box(rect, zCells)
	if OUTPUTRESULT && usedBox && (!usedLine || !slices.Equal(resultLine.Removed, resultBox.Removed)) {
		result = resultBox
		if usedLine {
			result = mergeResults(resultLine, resultBox)
		}
	}

	used = usedLine || usedBox

	if used && OUTPUTRESULT {
		results = append(results, result)
	}

	return
}

func (s *Solve) UniqueRectangle8(g *Grid) (used bool, results []*Result) {
	return urTemplate8(g, g.doURType8)
}

func (g *Grid) getURP18(p0 *Cell) (p1s []*Cell) {
	for _, cell := range g.rows[p0.rowNum].cells {
		if cell.solved() || cell == p0 {
			continue
		}
		if cell.cands.Intersect(p0.cands).Count() != 1 {
			continue
		}
		p1s = append(p1s, cell)
	}
	return
}

func (g *Grid) getURP28(p0 *Cell) (p2s []*Cell) {
	for _, cell := range g.cols[p0.colNum].cells {
		if cell.solved() || cell == p0 {
			continue
		}
		if cell.cands != p0.cands {
			continue
		}
		p2s = append(p2s, cell)
	}
	return
}

func (g *Grid) getURP38(p0, p1, p2 *Cell) *Cell {
	p3 := g.rows[p2.rowNum].cells[p1.colNum]
	if p3.solved() {
		return nil
	}

	if p3.cands.Intersect(p0.cands).Count() == 0 {
		return nil
	}

	return p3
}

func (g *Grid) getUR8() (rects [][]*Cell) {
	p0s := g.getURP0()
	for _, p0 := range p0s {
		p1s := g.getURP18(p0)
		p2s := g.getURP28(p0)
		for _, p1 := range p1s {
			for _, p2 := range p2s {
				if !withinTwoBoxes([]int{p0.rowNum, p2.rowNum}, []int{p0.colNum, p1.colNum}) {
					continue
				}
				p3 := g.getURP38(p0, p1, p2)
				if p3 != nil {
					rects = append(rects, []*Cell{p0, p1, p2, p3})
				}
			}
		}
	}
	return
}

func urTemplate8(g *Grid, doURType func([]*Cell) (bool, []*Result)) (used bool, results []*Result) {
	rects := g.getUR8()
	for _, rect := range rects {
		if ok, rs := doURType(rect); ok {
			used = true
			if OUTPUTRESULT {
				results = append(results, rs...)
			}
			if !EXHAUSTIVE {
				return
			}
		}
	}
	return
}

func (g *Grid) doURType8(rect []*Cell) (used bool, results []*Result) {
	//fmt.Println("@@@@@@", rect)
	p0, p1, p2, p3 := rect[0], rect[1], rect[2], rect[3]
	x, y := p0.cands.GetList()[0], p0.cands.GetList()[1]

	var strongs []*Cell
	var strongCand int
	var rmCell *Cell
	var rmCand int
	if p1.cands.Has(x) && onlyCellsHasCand(x, g.rows[p0.rowNum], []*Cell{p0, p1}) {
		strongs = []*Cell{p0, p1}
		strongCand = x
		rmCell = p3
		rmCand = y
	} else if p1.cands.Has(y) && onlyCellsHasCand(y, g.rows[p0.rowNum], []*Cell{p0, p1}) {
		strongs = []*Cell{p0, p1}
		strongCand = y
		rmCell = p3
		rmCand = x
	}

	if len(strongs) == 0 {
		return
	}

	if !p3.cands.Has(rmCand) {
		return
	}

	used = true

	if OUTPUTRESULT {
		result := new(Result)
		result.addRemoved(set.NewSet(rmCand), []*Cell{rmCell})
		g1 := result.getHelpCandsGroup()
		g1.add(p0.cands, []*Cell{p0, p2})
		link := fmt.Sprintf("R%vC%v[%v]=R%vC%v[%v]",
			p0.rowNum+1, p0.colNum+1, strongCand+1,
			p1.rowNum+1, p1.colNum+1, strongCand+1)
		result.addHelpChains(link)
		result.setStrategyName("Unique Rectangle Type4D")
		results = append(results, result)
	}
	if CHANGE {
		removeCands(set.NewSet(rmCand), []*Cell{rmCell})
	}

	return

}
